<?php
declare(strict_types=1);

namespace Service\PayService\Payer;

use Service\BaseService;
use Service\Code\Normal;
use App\Model\BbcModel\Order;
use Service\VenderClient\PayCenterApi\WeixinApiClient;
use Service\UserService\MnUserService;
use Service\ChannelService\StoreConfigService;
use Service\ConfigureService\ChannelConfigService;
use Service\DeclareService\PayDeclareService;
use Service\MessageService\PayNotifyService;
use Service\CacheService\CacheProviderService;
use Service\PayService\CashPayService;

/**
 * 微信支付
 * @author chenshubo
 */
class Wxmnapp extends BaseService
{
    public $checktime= 200;
    
    /**
     * 创建支付订单
     * @param int $cid
     * @param int $mn_sid
     * @param array $extra
     */
    public function userPay(int $cid,int $mn_sid,int $mnid,array $extra = [])
    {
        $result = $this->getConfig($cid, $mn_sid, $mnid);
        if( $result['code'] != Normal::SUC ) {
            return $result;
        }
        $config = $result['data'];
        
        //获取回调
        $callback_url = PayNotifyService::instance()->getChannelNotifyHost($cid);
        if( empty($callback_url) ) {
            return Normal::returnCode(Normal::FAIL,"微信小程序支付回调地址未配置");
        }
        $noticeUrl = "http://{$callback_url}/webapi/pay/notify/{$cid}/{$mn_sid}/{$mnid}/wxmnapp/{$extra['order_no']}";
        $order = [
            'attach'            => "在线支付",
            'out_trade_no'      => $extra['order_no'],
            'notify_url'        => $noticeUrl,
            'total_fee'         => bcmul((string)$extra['money'],'100'),
            'trade_type'        => 'JSAPI',
            'openid'            => $extra['open_id']
        ];
        $client = (new WeixinApiClient($config));
        $result = $client->mnAppPay($order);
        if( $result['code'] != Normal::SUC ) {
            return $result;
        }
        
        return Normal::returnCode(Normal::SUC,"处理成功",[
            'payInfo'       => $result['data'],
            'payRequest'    => json_encode($order,JSON_UNESCAPED_UNICODE),
            'payResponse'   => json_encode($result['data'],JSON_UNESCAPED_UNICODE),
        ]);
    }
    
    /**
     * 收银扫码支付
     * @param int $cid
     * @param int $mn_sid
     * @param array $extra
     */
    public function scanPay(int $cid,int $mn_sid, int $mnid,array $param)
    {
        $store_type = $param['store_type'];
//        if( $store_type == MnUserService::TYPE_O2O ) {
//            $payconfig = ChannelConfigService::instance()->getChannelConfig('wx_scan_gun_pay', $cid);
//        }
//        elseif( $store_type == MnUserService::TYPE_QZ ) {
//            $payconfig = StoreConfigService::instance()->getConfig('wx_scan_gun_pay', $mn_sid);
//        }
        $payconfig = StoreConfigService::instance()->getConfig('wx_scan_gun_pay', $mn_sid);
        
        if(empty($payconfig)) {
            return Normal::returnCode(Normal::FAIL,"微信扫码支付未配置");
        }
        if( substr($param['auth_code'], 0,2) >= 20 ) {
            return Normal::returnCode(Normal::FAIL, "当前客户订单支付结算仅支持【微信付款码】进行付款,请收银员核对客户出示的付款码[{$param['auth_code']}]是否正确，感谢您的配合！");
        }
        
        //获取回调,收银扫码支付无需回调
//        $callback_url = PayNotifyService::instance()->getChannelNotifyHost($cid);
//        if( empty($callback_url) ) {
//            return Normal::returnCode(Normal::FAIL,"微信小程序支付回调地址未配置");
//        }
//        $path=request()->getRequestUri();   
//        $url=request()->url(); 
//        $callback_url=str_replace($path, '', $url);
//        $noticeUrl = "{$callback_url}/webapi/pay/notify/{$cid}/{$mn_sid}/{$mnid}/wxmnapp/{$param['out_trade_no']}";
        $noticeUrl='';
        $out_trade_no = $param['out_trade_no'];
        $order = [
            'auth_code'     => $param['auth_code'],
            'body'          => $param['body'],
            'out_trade_no'  => $out_trade_no,
            'total_fee'     => $param['total_fee'],
            'notify_url'    => $noticeUrl,
            'trade_type'    => 'MICROPAY',
            'device_info'   => 'POS',
            'attach'        => '收银机扫码支付'
        ];
        
        //支付单申报
        $payreport_data = [];
        $is_order = $param['is_order'];
        if($store_type == MnUserService::TYPE_QZ && $is_order == 1 )
        {
            $companyConfig = StoreConfigService::instance()->getConfig('e_company', $mn_sid);
            if(empty($companyConfig)) {
                return Normal::returnCode(Normal::FAIL,"电商平台未配置");
            }
            $payreport_data= [
                'cid'           => $cid,
                'company'       => $companyConfig,
                'payconfig'     => $payconfig,
                'pay_type'      => 'wechatpay',
                'pay_serial'    => '',
                'pay_sn'        => $out_trade_no
            ];
        }
        
        //调用统一下单接口
        $initalRequest = json_encode($order,JSON_UNESCAPED_UNICODE);
        $client = (new WeixinApiClient($payconfig));
        $result_call = $client->scanPayOrder($order);        
        if( $result_call['code'] != Normal::SUC )
        {
            //判断用户是否正在支付中的情况
            if( ! (isset($result_call['data']['err_code']) && $result_call['data']['err_code'] == 'USERPAYING') ) {
                return $result_call;
            }
            
            $order['time']              = 0;
            $order['payreport_data']    = $payreport_data;
            $order['initalRequest']     = $initalRequest;
            $order['is_order']          = $is_order;
            $order['store_type']        =$store_type;
            return $this->checkWeixin($order,$payconfig);
        }
        
        //做支付单申报
        if( $store_type == MnUserService::TYPE_QZ && $is_order == 1 ) {
            $payreport_data['pay_serial'] = $result_call['data']['transaction_id'];
            //PayDeclareService::instance()->publishMsg($payreport_data);
            $result_call['data']['payreport_data']=$payreport_data;
        }
        $result_call['data']['initalResponse']  = json_encode($result_call['data'],JSON_UNESCAPED_UNICODE);
        $result_call['data']['initalRequest']   = $initalRequest;
        $result_call['data']['auth_user_pay_type']   = CashPayService::AUTH_USER_PAY_WEIXIN;
        $result_call['data']['pay_time']   = $result_call['data']['time_end']?strtotime($result_call['data']['time_end']):0;
        return $result_call;
    }
    
    /**
     * 等待微信支付密码输入成功
     * @param array $param
     */
    public function checkWeixin( array $param = [],array $payconfig=[] )
    {
        sleep(5);
        
        $param['time'] += 5;
        $client = (new WeixinApiClient($payconfig));
        $result_call = $client->queryPay(['out_trade_no'=>$param['out_trade_no']]); 
        
        if(isset($result_call['data']['transaction_id'])){
            if($param['store_type']==MnUserService::TYPE_QZ&&$param['is_order']==1){
                
                $report_data=$param['payreport_data'];
                $report_data['pay_serial']=$result_call['data']['transaction_id'];
                //PayDeclareService::instance()->publishMsg($report_data);
                $result_call['data']['payreport_data']=$report_data;
                
            }
            $result_call['data']['initalResponse']=json_encode($result_call['data'],JSON_UNESCAPED_UNICODE);
            $result_call['data']['initalRequest']=$param['initalRequest'];
            $result_call['data']['auth_user_pay_type']   =CashPayService::AUTH_USER_PAY_WEIXIN;
            $result_call['data']['pay_time']   = $result_call['data']['time_end']?strtotime($result_call['data']['time_end']):0;
            
            return $result_call;
        }elseif($result_call['data']['trade_state']=='USERPAYING'){
            if($param['time']>=$this->checktime){
                return Normal::returnCode(Normal::FAIL,"交易超时,请重试");
            }else{
                return $this->checkweixin($param,$payconfig);
            }
        }else{
            return Normal::returnCode(Normal::FAIL,$result_call['data']['trade_state_desc']);
        }
    }
    
    /**
     * 微信扫码支付退款
     * @param int $cid
     * @param int $mn_sid
     * @param int $mnid
     * @param array $param
     * @return type
     */
    public function scanPayRefund(int $cid,int $mn_sid, int $mnid,array $param) {
        $store_type = $param['store_type'];
//        if( $store_type == MnUserService::TYPE_O2O ) {
//            $payconfig = ChannelConfigService::instance()->getChannelConfig('wx_scan_gun_pay', $cid);
//        }
//        elseif( $store_type == MnUserService::TYPE_QZ ) {
//            $payconfig = StoreConfigService::instance()->getConfig('wx_scan_gun_pay', $mn_sid);
//        }
        $payconfig = StoreConfigService::instance()->getConfig('wx_scan_gun_pay', $mn_sid);
        if(empty($payconfig)) {
            return Normal::returnCode(Normal::FAIL,"微信扫码支付未配置");
        }
        $out_trade_no = $param['out_trade_no'];
        $order = [
            'out_refund_no'     => $param['out_refund_no'],
            'body'          => $param['body'],
            'out_trade_no'  => $out_trade_no,
            'total_fee'     => $param['total_fee'],
            'refund_fee'    => $param['refund_fee'] ?? $param['total_fee'],
        ];
        $client = (new WeixinApiClient($payconfig));
        $result_call = $client->scanPayRefund($order); 
        return $result_call;
    }
    
    /**
     *  解析body内容
     * @param $body
     */
    public function getNotify($body) 
    {
        //解析回调body内容
    }
    

    /**
     * 获取微信小程序OPEN_ID
     * @param int $cid
     * @param int $mn_sid
     * @param int $mnid
     * @param string $js_code
     */
    public function codeToSession(int $cid,int $mn_sid,int $mnid,string $js_code)
    {
        $result = $this->getConfig($cid, $mn_sid, $mnid);
        if( $result['code'] != Normal::SUC ) {
            return $result;
        }
        $config = $result['data'];
        return (new WeixinApiClient($config))->getOpenID($js_code);
    }
    
    /**
     * 获取授权用户信息，需要用户关注公众号
     */
    public function getSubscribe(int $cid,int $mn_sid,int $mnid,string $open_id)
    {
        $result = $this->getConfig($cid, $mn_sid, $mnid);
        if( $result['code'] != Normal::SUC ) {
            return $result;
        }
        $config = $result['data'];
        
        $client = new WeixinApiClient($config);
        
        $key = md5(implode("_",["user_wx_access_token",$cid,$mn_sid,$mnid,$open_id]));
        $access_result  = CacheProviderService::getCacheByKey($key);
        if(empty($access_token)) {
            $access_result = $client->getAccessToken();
            if( $access_result['code'] != Normal::SUC ) {
                return $access_result;
            }
            CacheProviderService::cacheBySetKeyValue($key, $access_result,3600);
        }
        
        $access_token = $access_result['data']['access_token'];
        return $client->getSubscribe($access_token,$access_token);
    }
    
    /**
     * 获取刷脸支付调用凭证
     * @param int $cid
     * @param int $mn_sid
     * @param array $extra
     */
    public function userAuthInfo(int $cid,int $mn_sid,int $mnid,array $extra = [])
    {
        $store = MnUserService::instance()->getMnUserInfoByMnSid($mn_sid);
        $store_type = $store['type'];
        $config = StoreConfigService::instance()->getConfig('wx_scan_gun_pay', $mn_sid);
        if(empty($config)) {
            return Normal::returnCode(Normal::FAIL,"微信扫码支付未配置");
        }
        
        $param = [
            'store_id'     => $extra['store_id'],
            'store_name'   => $extra['store_name'],
            'device_id'    => $extra['device_id'],
            'rawdata'      => $extra['rawdata'],
        ];
        $client = new WeixinApiClient($config);
        return $client->mnAppgetUserAuth($param);
    }
    
    /**
     * 获取基础配置
     * @param int $cid
     * @param int $mn_sid
     * @param int $mnid
     * @return int[]|string[]|array[]
     */
    private function getConfig(int $cid,int $mn_sid,int $mnid)
    {
        $config = ChannelConfigService::instance()->getChannelConfig('wxmnapp_pay', $cid);
        if(empty($config)) {
            return Normal::returnCode(Normal::FAIL,"微信小程序支付未配置");
        }
        
        //判断TO C 还是TO B
        if( $mnid == TO_C_MNID_TYPE ) {
            $config['appid']        = $config['cappid'];
            $config['appsecret']    = $config['cappsecret'];
        }
        
        return Normal::returnCode(Normal::SUC,"获取配置成功",$config);
    }
}
